﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Microsoft.TeamFoundation.Build.Client;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Build.Common;
using Microsoft.Office.Interop.Outlook;
using Outlook.WorkItems.Extension.Manager;

namespace Outlook.WorkItems.Extension.Managers.TeamBuildManager
{
    public class SynchronizeBuildAction : IAction<MAPIFolder>
    {
        public BuildCalendarLink ParentLink { get; set; }
        public Dictionary<BuildStatus, string> CategoryByStatus { get; set; }
        public string BuildDefinitionName { get; set; }

        public SynchronizeBuildAction()
        {
            CategoryByStatus = new Dictionary<BuildStatus, string>();
            BuildDefinitionName = "";
        }

        public void DeserializeAction(XElement action)
        {
            BuildDefinitionName = action.Attribute("buildDefinitionName").Value;

            foreach (var item in action.Element("CategoriesByStatus").Elements("Status"))
            {
                CategoryByStatus.Add((BuildStatus)Enum.Parse(typeof(BuildStatus), item.Attribute("name").Value, true), item.Attribute("value").Value);
            }
        }

        public void DoAction(MAPIFolder folder)
        {
            if (folder == null || folder.DefaultItemType != OlItemType.olAppointmentItem)
                throw new ArgumentException("item must be a MAPIFolder Type and DefaultItemType must be olAppointmentItem");

            var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(ParentLink.TeamServerUri));
            var bserver = tfs.GetService<IBuildServer>();
            var details = bserver.QueryBuilds(ParentLink.TeamProjectName);
            var buildUris = new List<string>();
            var definitionBuildUris = new Dictionary<string, AppointmentItem>();
            
            foreach (AppointmentItem item in folder.Items)
            {
                var prop = item.UserProperties.Find("BuildUri");
                if (prop != null && !buildUris.Contains(prop.Value.ToString()))
                    buildUris.Add(prop.Value.ToString());

                var prop2 = item.UserProperties.Find("BuildDefinitionUri");
                if (prop2 != null && !definitionBuildUris.ContainsKey(prop2.Value.ToString()))
                    definitionBuildUris.Add(prop2.Value.ToString(), item);
            }

            SynchronizeBuildDefinitions(folder, bserver, definitionBuildUris);

            SynchronizeBuilds(folder, details, buildUris);
        }

        private void SynchronizeBuilds(MAPIFolder folder, IBuildDetail[] details, List<string> buildUris)
        {
            foreach (var item in details)
            {
                var buildUri = ParentLink.TeamServerUri + "#" + item.Uri;

                if (buildUris.Contains(buildUri) || (!string.IsNullOrEmpty(BuildDefinitionName) && item.BuildDefinition.Name != BuildDefinitionName))
                    continue;

                var appt = folder.Items.Add(OlItemType.olAppointmentItem) as AppointmentItem;
                if (appt == null) 
                    continue;

                appt.Start = item.StartTime;
                appt.End = item.FinishTime;
                appt.Subject = item.BuildDefinition.Name + " - " + item.Status.ToString();
                appt.ReminderSet = false;
                appt.UserProperties.Add("BuildUri", OlUserPropertyType.olText);
                appt.UserProperties["BuildUri"].Value = buildUri;

                try
                {
                    var body = "";
                    // Numéro de la Build
                    body = "Build Number : " + item.BuildNumber + Environment.NewLine;
                    // Emplacement du Log
                    body += "Log File : " + item.DropLocation + Environment.NewLine;

                    body += Environment.NewLine;
                    body += string.Format("Build last modified by {0} {1:0.0} hours ago", item.LastChangedBy, (DateTime.Now - item.LastChangedOn).TotalHours);
                    body += Environment.NewLine;

                    // WorkItem créé
                    foreach (var node in item.Information.GetNodesByType(InformationTypes.OpenedWorkItem, true))
                    {
                        body += string.Format("Bug {0}", node.Fields[InformationFields.WorkItemId]);
                        body += string.Format("Current state is {0}, currently assigned to {1}", node.Fields[InformationFields.Status], node.Fields[InformationFields.AssignedTo]);
                        body += Environment.NewLine;
                    }

                    // Configuration de la Build
                    var configuration = item.Information.GetNodesByType(InformationTypes.ConfigurationSummary, true).FirstOrDefault();
                    if (configuration != null)
                    {
                        // Plateforme
                        body += configuration.Fields[InformationFields.Platform] + " | " + configuration.Fields[InformationFields.Flavor] + Environment.NewLine;
                        // Nombre d'erreurs et de warnings
                        body += configuration.Fields[InformationFields.TotalCompilationErrors] + " error(s), " + configuration.Fields[InformationFields.TotalCompilationWarnings] + " warning(s) " + Environment.NewLine;
                    }
                    foreach (IBuildInformationNode item2 in item.Information.GetNodesByType(InformationTypes.BuildError, true))
                    {
                        body += "    ";
                        if (item2.Fields.ContainsKey(InformationFields.File))
                            body += item2.Fields[InformationFields.File];
                        if (item2.Fields.ContainsKey(InformationFields.LineNumber))
                            body += " (" + item2.Fields[InformationFields.LineNumber] + "): ";
                        if (item2.Fields.ContainsKey(InformationFields.Message))
                            body += item2.Fields[InformationFields.Message] + Environment.NewLine + Environment.NewLine;
                    }

                    foreach (IBuildInformationNode item2 in item.Information.GetNodesByType(InformationTypes.BuildWarning, true))
                    {
                        body += "    ";
                        if (item2.Fields.ContainsKey(InformationFields.File))
                            body += item2.Fields[InformationFields.File];
                        if (item2.Fields.ContainsKey(InformationFields.LineNumber))
                            body += " (" + item2.Fields[InformationFields.LineNumber] + "): ";
                        if (item2.Fields.ContainsKey(InformationFields.Message))
                            body += item2.Fields[InformationFields.Message] + Environment.NewLine + Environment.NewLine;
                    }

                    appt.Body = body;
                }
                catch { }
                
                switch (item.Status)
                {
                    case BuildStatus.Failed:
                        appt.Categories = CategoryByStatus[BuildStatus.Failed];
                        break;
                    case BuildStatus.InProgress:
                        appt.Categories = CategoryByStatus[BuildStatus.InProgress];
                        break;
                    case BuildStatus.PartiallySucceeded:
                        appt.Categories = CategoryByStatus[BuildStatus.PartiallySucceeded];
                        break;
                    case BuildStatus.Stopped:
                        appt.Categories = CategoryByStatus[BuildStatus.Stopped];
                        break;
                    case BuildStatus.Succeeded:
                        appt.Categories = CategoryByStatus[BuildStatus.Succeeded];
                        break;
                    default:
                        break;
                }
                appt.Save();
            }
        }

        private void SynchronizeBuildDefinitions(MAPIFolder folder, IBuildServer bserver, Dictionary<string, AppointmentItem> definitionBuildUris)
        {
            var definitions = new List<IBuildDefinition>();
            if (!string.IsNullOrEmpty(BuildDefinitionName))
            {
                var bd = bserver.GetBuildDefinition(ParentLink.TeamProjectName, BuildDefinitionName);
                definitions.Add(bd);
            }
            else
            {
                var bd = bserver.QueryBuildDefinitions(ParentLink.TeamProjectName, QueryOptions.All);
                definitions.AddRange(bd);
            }

            foreach (var bd in definitions)
            {
                if (bd.ContinuousIntegrationType != ContinuousIntegrationType.Schedule &&
                    bd.ContinuousIntegrationType != ContinuousIntegrationType.ScheduleForced) 
                    continue;

                var buildDefinitionUri = ParentLink.TeamServerUri + "#" + bd.Uri;
                AppointmentItem appt = null;
                if (definitionBuildUris.ContainsKey(buildDefinitionUri))
                {
                    appt = definitionBuildUris[buildDefinitionUri];
                }

                foreach (var schedule in bd.Schedules)
                {
                    if (appt == null)
                    {
                        appt = folder.Items.Add(OlItemType.olAppointmentItem) as AppointmentItem;
                        if (appt != null)
                        {
                            appt.ReminderSet = false;
                            appt.UserProperties.Add("BuildDefinitionUri", OlUserPropertyType.olText);
                            appt.UserProperties["BuildDefinitionUri"].Value = buildDefinitionUri;
                            appt.AllDayEvent = false;
                        }
                    }

                    UpdateSchedulePattern(bd, appt, schedule);
                    if (appt != null) 
                        appt.Save();
                }
            }
        }

        private static void UpdateSchedulePattern(IBuildDefinition bd, AppointmentItem appt, ISchedule schedule)
        {
            appt.Subject = "Recurrent Build Definition : " + bd.Name;
            var pattern = appt.GetRecurrencePattern();
            pattern.RecurrenceType = OlRecurrenceType.olRecursWeekly;
            pattern.NoEndDate = true;
            pattern.Interval = 1;
            var daysOfWeek = OlDaysOfWeek.olFriday;
            if ((schedule.DaysToBuild & ScheduleDays.Friday) == ScheduleDays.Friday)
                daysOfWeek = OlDaysOfWeek.olFriday;
            else
                daysOfWeek |= ~OlDaysOfWeek.olFriday;
            if ((schedule.DaysToBuild & ScheduleDays.Monday) == ScheduleDays.Monday)
                daysOfWeek |= OlDaysOfWeek.olMonday;
            if ((schedule.DaysToBuild & ScheduleDays.Saturday) == ScheduleDays.Saturday)
                daysOfWeek |= OlDaysOfWeek.olSaturday;
            if ((schedule.DaysToBuild & ScheduleDays.Sunday) == ScheduleDays.Sunday)
                daysOfWeek |= OlDaysOfWeek.olSunday;
            if ((schedule.DaysToBuild & ScheduleDays.Thursday) == ScheduleDays.Thursday)
                daysOfWeek |= OlDaysOfWeek.olThursday;
            if ((schedule.DaysToBuild & ScheduleDays.Tuesday) == ScheduleDays.Tuesday)
                daysOfWeek |= OlDaysOfWeek.olTuesday;
            if ((schedule.DaysToBuild & ScheduleDays.Wednesday) == ScheduleDays.Wednesday)
                daysOfWeek |= OlDaysOfWeek.olWednesday;

            pattern.DayOfWeekMask = daysOfWeek;
            DateTime startDate = DateTime.Now.Date;
            pattern.StartTime = startDate.AddSeconds(schedule.StartTime);
            pattern.EndTime = pattern.StartTime.AddMinutes(30);
            pattern.PatternStartDate = DateTime.Now;
        }

        public XNode SerializeAction()
        {
            return new XElement("SynchronizeBuildAction",
                       new XAttribute("buildDefinitionName", BuildDefinitionName ?? ""),
                       new XElement("CategoriesByStatus",
                           from pair in CategoryByStatus
                           select new XElement("Status",
                                        new XAttribute("name", pair.Key.ToString()),
                                        new XAttribute("value", pair.Value))));
        }
    }
}
